home *** CD-ROM | disk | FTP | other *** search
- /* ∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞
-
- CFileTransfer.c
-
- CommToolbox file transfer class.
-
- SUPERCLASS = CBureaucrat.
-
- Copyright © 1992-93 Romain Vignes. All rights reserved.
-
- ∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞ */
-
-
- #include <CommResources.h> /* Apple includes */
-
- #include <CBartender.h> /* TCL includes */
- #include <CCluster.h>
- #include <CError.h>
- #include <Constants.h>
- #include <TBUtilities.h>
- #include <TCLUtilities.h>
-
- #include "CFileTransfer.h" /* Other includes */
-
-
- /* Constants & Macros */
-
- #define FTRANS_STR_RES_ID 2300 /* Transfer messages resource ID */
-
- #define NO_TOOL_STR_INDEX 1 /* No transfer tool */
- #define BAD_TOOL_STR_INDEX 2 /* Bad transfer tool */
- #define NO_REC_STR_INDEX 3 /* Transfer record allocation error */
- #define CHOOSE_STR_INDEX 4 /* tool setup error */
- #define START_ERR_STR_INDEX 5 /* transfer start error */
- #define SUCCESS_STR_INDEX 6 /* Transfer is successful */
- #define RUN_ERR_STR_INDEX 7 /* Transfer failed */
-
- #define H_CHOOSE_POS 10 /* Setup dialog position */
- #define V_CHOOSE_POS 40
-
- #define TEXT_FILE_TYPE 'TEXT'
-
-
- /* Application globals */
-
- extern CBartender *gBartender;
- extern CError *gError;
-
-
- /* Class variables initialization */
-
- CCluster *CFileTransfer::cFTransList = NULL;
-
-
- /*
- * cIsFileTransferCmd
- *
- * File transfer related command ?
- *
- * theCmd: the command to be analysed
- *
- * Return TRUE if the command is file transfer related
- *
- */
-
- Boolean CFileTransfer::cIsFileTransferCmd(long theCmd)
- {
- return ((theCmd >= cmdFTransChoose) && (theCmd <= cmdFTransRecv));
- }
-
-
- /* ∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞ */
-
-
- /*
- * cInitManager
- *
- * File Transfer Manager Initialization
- *
- */
-
- void CFileTransfer::cInitManager(void)
- {
- InitFT();
- }
-
-
- /* ∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞ */
-
- /*
- * cGetCMVersion
- *
- * return the version of the File Transfer Manager
- *
- */
-
- short CFileTransfer::cGetFTVersion(void)
- {
- return FTGetFTVersion();
- }
-
-
- /* ∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞ */
-
- /*
- * cCheckToolName
- *
- * Checking of the tool existence
- *
- * toolName: name of the tool (pascal string)
- *
- * Return an error code
- *
- */
-
- OSErr CFileTransfer::cCheckToolName(Str31 toolName)
- {
- return(FTGetProcID(toolName));
- }
-
-
- /* ∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞ */
-
- /*
- * cFTransIdle
- *
- * Idle time for each file transfer object
- *
- *
- */
-
- /* Idle routine for each file transfer object */
-
- static void FTrans_Idle(CFileTransfer *theFTrans)
- {
- theFTrans->DoIdle();
- }
-
-
- void CFileTransfer::cFTransIdle(void)
- {
- if (cFTransList != NULL) /* List exists ? */
- cFTransList->DoForEach((EachFunc) FTrans_Idle);
- }
-
-
- /* ∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞ */
-
- /*
- * cTestToolMenu
- *
- * Test if the selected menu belongs to a file transfer tool
- *
- * theMenu: selected menu ID
- * theItem: selected item ID
- *
- * Return TRUE if the menu is a file transfer tool menu
- */
-
-
- /* Test routine */
-
- static Boolean FTransTest(CFileTransfer *theFTrans)
- {
- return theFTrans->active;
- }
-
-
- Boolean CFileTransfer::cTestToolMenu(short theMenu, short theItem)
- {
- CFileTransfer *current;
-
- if (cFTransList == NULL)
- return FALSE;
- else {
- current = (CFileTransfer *) cFTransList->FindItem((TestFunc) FTransTest);
-
- if (current == NULL)
- return FALSE;
- else
- return current->DoMenu(theMenu,theItem);
- }
- }
-
-
- /* ∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞ */
-
- /*
- * cTestToolEvent
- *
- * Test if the event is related to a file transfer tool
- *
- * macEvent: pointer on the event record
- * theWindow: pointer on the window record
- *
- * Return TRUE if the event is a file transfer tool event
- */
-
- typedef struct {
- EventRecord *theEvent;
- WindowPtr theWindow;
- Boolean isToolEvent;
- } TestParamRec;
-
-
- /* Test routine */
-
- static void FTransEvtTest(CFileTransfer *theFTrans,TestParamRec *params)
- {
- if (params->isToolEvent == FALSE)
- params->isToolEvent = theFTrans->DoEvent(params->theEvent,
- params->theWindow);
- }
-
-
- Boolean CFileTransfer::cTestToolEvent(EventRecord *macEvent,WindowPtr theWindow)
- {
- TestParamRec params;
-
- params.theEvent = macEvent;
- params.theWindow = theWindow;
- params.isToolEvent = FALSE;
-
- if (cFTransList == NULL)
- return FALSE;
- else {
- cFTransList->DoForEach1((EachFunc1) FTransEvtTest,(long) ¶ms);
-
- return params.isToolEvent;
- }
- }
-
-
- /* ∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞ */
-
- /*
- * IFileTransfer
- *
- * File transfer object initialization
- *
- * aSupervisor: Supervisor in the chain of commands
- * toolName: Name of tool to use ("" -> default)
- * flags:(FT) Tool use flag
- * sendProc:(FT) Chars sending proc
- * recvProc:(FT) Chars receiving proc
- * readProc:(FT) Chars reading proc
- * writeProc:(FT) Chars writing proc
- * environsProc:(FT) Environment description proc
- * refcon:(FT) available for the application
- * userData:(FT) available for the application
- *
- * Parameters followed by FT are exact required parameters for creating a
- * file transfer record.
- *
- */
-
- void CFileTransfer::IFileTransfer(CBureaucrat *aSupervisor,Str31 toolName,
- FTFlags flags,ProcPtr sendProc,ProcPtr recvProc,
- ProcPtr readProc,ProcPtr writeProc,ProcPtr environsProc,
- WindowPtr owner,long refCon, long userData)
- {
- FTHandle theFTrans;
- OSErr theErr;
- Str31 tName;
- short toolProcID;
- Boolean savedAlloc;
-
- CBureaucrat::IBureaucrat(aSupervisor); /* Initialize its superclass */
-
- if (toolName[0] == 0) { /* Default tool ? */
-
- theErr = CRMGetIndToolName(classFT,1,tName);
-
- if ((theErr != ftNoErr) || (tName[0] == 0)) /* No tool */
- Failure(ftNoTools,SpecifyMsg(FTRANS_STR_RES_ID,NO_TOOL_STR_INDEX));
-
- toolProcID = FTGetProcID(tName); /* Default tool ID */
- }
- else
- toolProcID = FTGetProcID(toolName); /* Chosen tool ID */
-
- if (toolProcID == cmGenericError) /* No tool */
- Failure(ftNoTools,SpecifyMsg(FTRANS_STR_RES_ID,BAD_TOOL_STR_INDEX));
-
- savedAlloc = SetAllocation(kAllocCanFail);
-
- theFTrans = FTNew(toolProcID,flags,sendProc,recvProc,readProc,writeProc,
- environsProc,owner,refCon,userData);
-
- SetAllocation(savedAlloc);
-
- FailNIL(theFTrans); /* File transfer record created ? */
-
- MoveHHi((Handle)theFTrans); /* Heap fragmentation… */
-
- itsFTrans = theFTrans; /* Instance variable */
-
- if (cFTransList == NULL) { /* FIrst transfer object ? */
- cFTransList = new(CCluster);
- cFTransList->ICluster();
- }
-
- cFTransList->Add(this); /* Transfer addition */
-
- this->wasFTMode = FALSE;
-
- this->active = FALSE;
- }
-
-
- /* ∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞ */
-
- /*
- * Dispose
- *
- * File transfer object removal
- *
- */
-
- void CFileTransfer::Dispose(void)
- {
- ASSERT(cFTransList != NULL);
-
- cFTransList->Remove(this); /* Dispose of the file transfer */
-
- if (cFTransList->IsEmpty())
- ForgetObject(cFTransList); /* Dispose of the cluster */
-
- FTDispose(itsFTrans); /* Transfer record disposal */
- itsFTrans = NULL;
-
- inherited::Dispose(); /* Pass message to its superclass */
- }
-
-
- /* ∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞ */
-
- /*
- * UpdateMenus
- *
- * Transfer related menus updating
- *
- */
-
- void CFileTransfer::UpdateMenus(void)
- {
- if (!IsRunning()) {
-
- gBartender->EnableCmd(cmdFTransChoose); /* Setup command */
-
- if (!((*itsFTrans)->attributes & ftSendDisable))
- gBartender->EnableCmd(cmdFTransSend); /* Send command */
-
- if (!((*itsFTrans)->attributes & ftReceiveDisable))
- gBartender->EnableCmd(cmdFTransRecv); /* Receive command */
-
- }
- }
-
-
- /* ∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞ */
-
- /*
- * DoCommand
- *
- * Handle file transfer commands
- *
- * theCommand: command to be executed
- *
- */
-
- void CFileTransfer::DoCommand(long theCommand)
- {
- switch (theCommand) {
-
- case cmdFTransChoose: /* Transfer tool setup */
- this->FileTransferChoose();
- break;
-
- case cmdFTransSend: /* File sending */
- this->Start(ftTransmitting);
- break;
-
- case cmdFTransRecv: /* File receiving */
- this->Start(ftReceiving);
- break;
-
- default: /* Unknown command */
- break;
- }
- }
-
-
- /* ∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞ */
-
- /*
- * FileTransferChoose
- *
- * Transfer tool setup
- *
- */
-
- void CFileTransfer::FileTransferChoose(void)
- {
- short retCode;
- Point where;
- FTHandle hFTrans;
-
- hFTrans = this->itsFTrans;
- HUnlock((Handle)hFTrans);
-
- SetPt(&where,H_CHOOSE_POS,V_CHOOSE_POS); /* Dialog position */
-
- retCode = FTChoose(&hFTrans,where,NULL);
-
- HLock((Handle)hFTrans);
- this->itsFTrans = hFTrans;
-
- switch (retCode) {
- case chooseCancel: /* Setup cancelling */
- break;
-
- case chooseOKMinor: /* Same tool, changed config */
- case chooseOKMajor: /* Changed tool */
-
- active = FALSE;
- this->Activate(); /* Activation */
-
- itsSupervisor->Notify(NULL); /* Notify document update */
-
- break;
-
- default: /* Unknown code */
- SysBeep(3);
- gError->PostAlert(FTRANS_STR_RES_ID,CHOOSE_STR_INDEX);
- break;
- }
- }
-
-
- /* ∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞ */
-
- /*
- * SetConfig
- *
- * Modify file transfer tool config
- *
- * theConfig: configuration string (C string))
- *
- * Renvoie: negative value: error (-1 -> unknown)
- * positive value: parser stop index
- * cmNoErr if all is OK
- *
- */
-
- short CFileTransfer::SetConfig(char *theConfig)
- {
- short retCode;
-
- retCode = FTSetConfig(itsFTrans,theConfig);
-
- return retCode;
- }
-
-
- /* ∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞ */
-
- /*
- * GetToolName
- *
- * Return the file transfer tool name
- *
- * toolName: Name of the tool
- *
- */
-
- void CFileTransfer::GetToolName(Str31 toolName)
- {
- SignedByte savedState;
-
- savedState = HGetState(itsFTrans);
- HLock(itsFTrans);
-
- FTGetToolName((*itsFTrans)->procID,toolName);
-
- HSetState(itsFTrans,savedState);
- }
-
-
- /* ∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞ */
-
- /*
- * GetConfig
- *
- * Return the config string of the tool
- *
- * Return a pointer on a C string
- *
- */
-
- Ptr CFileTransfer::GetConfig(void)
- {
- return(FTGetConfig(itsFTrans));
- }
-
-
- /* ∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞ */
-
- /*
- * DoIdle
- *
- * Idle time of the application
- *
- */
-
- void CFileTransfer::DoIdle(void)
- {
-
- if (IsRunning())
- FTExec(itsFTrans); /* Transfert enactement */
-
- else if (this->wasFTMode) { /* Running ? */
-
- this->wasFTMode = FALSE; /* Transfer halted */
- }
-
- }
-
-
- /* ∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞ */
-
- /*
- * Activate
- *
- * Transfer activation
- *
- */
-
- void CFileTransfer::Activate(void)
- {
- if (!active) {
- FTActivate(itsFTrans,TRUE);
- active = TRUE;
- }
- }
-
-
- /* ∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞ */
-
- /*
- * Deactivate
- *
- * Transfert deactivation
- *
- */
-
- void CFileTransfer::Deactivate(void)
- {
- if (active) {
- FTActivate(itsFTrans,FALSE);
- active = FALSE;
- }
- }
-
-
- /* ∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞ */
-
- /*
- * IsRunning
- *
- * Return the state of the file transfer
- *
- * Renvoie TRUE if the transfer is running
- *
- */
-
- Boolean CFileTransfer::IsRunning(void)
- {
- return ((*itsFTrans)->flags & ftIsFTMode);
- }
-
-
- /* ∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞ */
-
- /*
- * Start
- *
- * File transfer startup
- *
- * direction: transfer direction
- *
- */
-
- void CFileTransfer::Start(FTDirection direction)
- {
- SFReply theReply;
- Point corner;
- short numTypes;
- SFTypeList typeList;
- OSErr theErr;
-
- switch (direction) {
-
- case ftTransmitting: /* Sending */
-
- if ((*itsFTrans)->attributes & ftTextOnly) { /* Text files only */
- typeList[0] = TEXT_FILE_TYPE;
- numTypes = 1;
- }
- else
- numTypes = -1; /* All types of file */
-
- FindDlogPosition('DLOG', getDlgID, &corner);
-
- SFPGetFile(corner,"\p", NULL, numTypes, typeList,
- NULL,&theReply, getDlgID, NULL);
-
- if (theReply.good) { /* Validate */
-
- theErr = FTStart(itsFTrans,ftTransmitting,&theReply);
-
- if (theErr == ftNoErr) { /* Erreur de lancement */
- this->wasFTMode = TRUE;
- }
- }
-
- break;
-
- case ftReceiving: /* Receiving */
-
- theReply.vRefNum = 0;
- theReply.fName[0] = 0;
-
- theErr = FTStart(itsFTrans,ftReceiving,&theReply);
-
- if (theErr == ftNoErr) { /* Erreur de lancement */
- this->wasFTMode = TRUE;
- }
-
- break;
-
- default: /* Other */
- break;
- }
- }
-
-
- /* ∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞ */
-
- /*
- * DoEvent
- *
- * Transfer related event handling
- *
- * theEvent: Pointer on the event record
- * theWindow: Window in which occured the event
- *
- * Renvoie TRUE if the event is handled by the tool
- *
- */
-
- Boolean CFileTransfer::DoEvent(EventRecord *theEvent,WindowPtr theWindow)
- {
- Boolean isToolEvent;
- FTHandle ftHdl;
-
- isToolEvent = FALSE;
-
- ftHdl = (FTHandle) GetWRefCon(theWindow);
-
- if (ftHdl == itsFTrans) { /* Tool window ? */
- FTEvent(itsFTrans,theEvent);
- isToolEvent = TRUE;
- }
-
- return isToolEvent;
- }
-
-
- /* ∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞ */
-
- /*
- * Success
- *
- * Get the ending status of the file transfer
- *
- * Renvoie TRUE if transfer was successfull
- *
- */
-
- Boolean CFileTransfer::Success(void)
- {
- return ((*itsFTrans)->flags & ftSucc);
- }
-
-
- /* ∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞ */
-
- /*
- * DoMenu
- *
- * Handle tool menus
- *
- * theMenu: selected menu
- * theItem: selected item
- *
- * Return TRUE if the menu belongs to the tool
- *
- */
-
- Boolean CFileTransfer::DoMenu(short theMenu,short theItem)
- {
- return(FTMenu(itsFTrans,theMenu,theItem));
- }
-
-
- /* ∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞ */
-